#include "cpu/cpu.h"
#include "os_cfg.h"
#include "common/cpu_instr.h"

static segment_desc_t gdt_table[GDT_TABLE_SIZE];

void segment_desc_set(int selector, uint32_t base, uint32_t limit, uint16_t attr)
{
    // 1. 根据selector计算GDT表中的对应项索引
    segment_desc_t *desc = gdt_table + (selector / sizeof(segment_desc_t));

    if(limit > 0xFFFFF)
    {
        // 如果段限长超过1MB，则启用4KB粒度
        limit = limit >> 12;
        attr |= 0x8000; // 设置G位
    }

    // 2. 拆分段限长：低16位存入limit15_0
    desc->limit15_0 = limit & 0xFFFF;
    // 3. 拆分段基址：低16位存入base15_0
    desc->base15_0 = base & 0xFFFF;
    // 4. 拆分段基址：中8位（16~23位）存入base23_16
    desc->base23_16 = (base >> 16) & 0xFF;
    // 5. 拼接属性和段限长的高4位，存入attr字段
    desc->attr = attr | (((limit >> 16) & 0xF) << 8);
    // 6. 拆分段基址：高8位（24~31位）存入base31_24
    desc->base31_24 = (base >> 24) & 0xFF;
}

void init_gdt(void)
{
    // 初始化GDT表
    for (int i = 0; i < GDT_TABLE_SIZE; i++)
    {
        segment_desc_set(i * sizeof(segment_desc_t), 0, 0, 0);
    }

    // 设置内核代码段描述符
    segment_desc_set(KERNEL_SELECTOR_CS, 0x00000000, 0xFFFFFFFF,
                     SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_CODE | SEG_TYPE_RW | SEG_D | SEG_G);

    // 设置内核数据段描述符
    segment_desc_set(KERNEL_SELECTOR_DS, 0x00000000, 0xFFFFFFFF,
                     SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_DATA | SEG_TYPE_RW | SEG_D | SEG_G);

    // 加载GDT表
    lgdt((uint32_t)gdt_table, sizeof(gdt_table));
}

void cpu_init(void)
{
    init_gdt();
}